package com.aiyun.jpa.web;

import com.aiyun.jpa.domain.Customer;
import com.aiyun.jpa.domain.QCustomer;
import com.aiyun.jpa.repository.CustomerQueryDslRepository;
import com.aiyun.jpa.repository.CustomerRepository;
import com.aiyun.jpa.service.dto.CustomerSearch;
import com.querydsl.core.types.dsl.BooleanExpression;
import org.slf4j.Logger;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.slf4j.LoggerFactory.getLogger;
import static org.springframework.data.jpa.domain.Specification.where;

@RestController
@RequestMapping("/api")
public class CustomerResource {

	private final Logger logger = getLogger(CustomerResource.class);

	private final CustomerRepository customerRepository;

	private final CustomerQueryDslRepository customerQueryDslRepository;



	public CustomerResource(CustomerRepository customerRepository, CustomerQueryDslRepository customerQueryDslRepository) {
		this.customerRepository = customerRepository;
		this.customerQueryDslRepository = customerQueryDslRepository;
	}


	private Specification<Customer> minAge(CustomerSearch customerSearch) {
		return ((root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.gt(root.get("age"), customerSearch.getMinAge()));
	}

	private Specification<Customer> maxAge(CustomerSearch customerSearch) {
		return ((root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.lt(root.get("age"), customerSearch.getMaxAge()));
	}

	@GetMapping("/customers")
	public ResponseEntity<Page<Customer>> getAll(CustomerSearch customerSearch, Pageable pageable) {
		logger.debug("使用Specification获取所有客户列表");
		Specification<Customer> specification = where(null);
		if (customerSearch.getMinAge() != null) {
			specification = specification.and(minAge(customerSearch));
		}

		if (customerSearch.getMaxAge() != null) {
			specification = specification.and(maxAge(customerSearch));
		}
		Page<Customer> page = customerRepository.findAll(
				specification,
				pageable
		);
		return ResponseEntity.ok(page);
	}

	@GetMapping("/customers/dsl")
	public ResponseEntity<Page<Customer>> queryDsl(CustomerSearch customerSearch, Pageable pageable) {
		logger.debug("使用DSL获取所有客户列表");
		QCustomer customer = QCustomer.customer;
		BooleanExpression expression = customer.isNotNull();
		if (customerSearch.getMaxAge() != null) {
			expression = expression.and(customer.age.lt(customerSearch.getMaxAge()));
		}


		if (customerSearch.getMinAge() != null) {
			expression = expression.and(customer.age.gt(customerSearch.getMinAge()));
		}
		Page<Customer> page = customerQueryDslRepository.findAll(expression, pageable);
		return ResponseEntity.ok(page);
	}


	/**
	 * Query By Example(QBE)查询，用于构建简单查询，无法用于复杂的嵌套查询，如果查询比较复杂，考虑使用specification或者DSL替代
	 * @param customerSearch
	 * @param pageable
	 * @return
	 */
	@GetMapping("/customers/example")
	public ResponseEntity<Page<Customer>> queryByExample(CustomerSearch customerSearch, Pageable pageable) {
		logger.debug("使用Query By Example获取所有客户列表");
		Customer customer = new Customer();
		customer.setCustomerName(customerSearch.getName());
		ExampleMatcher exampleMatcher = ExampleMatcher.matching()
				.withMatcher("customerName", GenericPropertyMatcher::startsWith);
		Example<Customer> example = Example.of(customer, exampleMatcher);
		Page<Customer> page = customerRepository.findAll(example, pageable);
		return ResponseEntity.status(HttpStatus.OK).body(page);
	}


	/**
	 * JPA Name Query，根据repository的方法名构建查询脚本，不适合复杂场景
	 * @param customerSearch
	 * @param pageable
	 * @return
	 */
	@GetMapping("/customers/name-query")
	public ResponseEntity<Page<Customer>> nameQuery(CustomerSearch customerSearch, Pageable pageable) {
		logger.debug("使用Name Query 查询所有客户列表");
		Page<Customer> page = customerRepository.findByCustomerNameStartingWith(customerSearch.getName(), pageable);
		return ResponseEntity.ok(page);
	}

	@PostMapping("/customers")
	public ResponseEntity<Customer> add(@RequestBody @Validated Customer customer) {
		customerRepository.save(customer);
		return ResponseEntity.ok(customer);
	}
}
